﻿import { Node } from 'cc';
import { RedDot } from '../../Component/RedDot';
import { LogUtil } from '../../../Util/LogUtil';
import { RedDotModel } from './RedDotModel';
import { RedDotType, TB } from '../../../Data/Types';
import { Singleton } from '../../Manager/BaseManager';
import { GameLayer } from '../../Config/BaseEnum';
import { LayerManager } from '../../Manager/LayerManager';
import { Tool } from '../../../Util/Tool';

export enum RedDotNames {
  level_last = 'level_last',
  level_next = 'level_next',
  level_guide = 'level_guide',
  level_sidebar = 'level_sidebar',
  passAwardItem0 = 'passAwardItem0',
  passAwardItem1 = 'passAwardItem1',
  passAwardItem2 = 'passAwardItem2',

  timeAwardItem0 = 'timeAwardItem0',
  timeAwardItem1 = 'timeAwardItem1',
  timeAwardItem2 = 'timeAwardItem2',
  timeAwardItem3 = 'timeAwardItem3',
  timeAwardItem4 = 'timeAwardItem4',

  energyBtnAd = 'energyBtnAd',
  energyBtnClaim = 'energyBtnClaim',
}

const TAG: string = 'RedDotManager';
/**红点管理单例类*/
export class RedDotManager extends Singleton {
  className: string = 'RedDotManager';
  /**红点运算单元结果值Map<红点运算单元, 红点运算单元结果值>*/
  private unitResultMap: Map<string, number>;

  /**标脏的红点运算单元Map<红点运算单元, 红点运算单元>*/
  private dirtyUnitMap: Set<string>;

  /**红点运算单元结果改变列表*/
  private tempChangedUnitResultList: Array<string>;

  /**结果变化的红点名数据Map<红点名, 红点名>*/
  private resultChangedRedDotMap: Map<string, string>;

  /**初始化红点数据*/
  initRedDotData() {
    this.unitResultMap = new Map<string, number>();
    this.dirtyUnitMap = new Set<string>();
    this.tempChangedUnitResultList = new Array<string>();
    this.resultChangedRedDotMap = new Map<string, string>();
    RedDotModel.Ins().initRedDotUnitInfo();
    this.doAllRedDotUnitCaculate();
  }

  /**
   * 执行所有红点名运算单元运算
   * 进入游戏后获取完相关数据后触发一次,确保第一次运算结果缓存
   * */
  doAllRedDotUnitCaculate() {
    const redDotUnitInfoMap = RedDotModel.Ins().getRedDotUnitInfoMap();
    redDotUnitInfoMap.forEach((v, k) => {
      this.unitResultMap.set(k, 0);
      this.doRedDotUnitCaculate(k);
    });
  }

  bindRedDotMap: Map<string, Node> = new Map<string, Node>();
  /**绑定指定红点名刷新回调*/
  bindRedDotName(redDotName: string, layer: GameLayer, refreshDelegate: (dotName: string, result: number, type: RedDotType) => void, target: unknown, size?: number) {
    if (this.bindRedDotMap.has(redDotName)) {
      LogUtil.w(TAG, `红点名:${redDotName}已经绑定过刷新回调,或者可以新加红点名`);
      return;
    }
    const dot = this.getRedDot(layer, size);
    const redDotInfo = RedDotModel.Ins().addRedDotInfo(redDotName);
    redDotInfo.Bind(refreshDelegate, target);
    const rr = this.getRedDotResultAndType(redDotName);
    const sp = dot.getComponent(RedDot);
    sp.setRedDotData(rr[0], rr[1]);
  }

  /**解绑定指定红点名*/
  unbindRedDotName(redDotName: string) {
    if (!this.bindRedDotMap.has(redDotName)) {
      LogUtil.w(TAG, `红点名:${redDotName}, 找不到绑定信息`);
      return;
    }

    const dot = this.bindRedDotMap.get(redDotName);
    LayerManager.Singleton.factory.push(dot);

    const redDotInfo = RedDotModel.Ins().getRedDotInfoByName(redDotName);
    if (redDotInfo) {
      redDotInfo.UnBind();
    } else {
      LogUtil.w(TAG, `找不到红点名:${redDotName}红点信息,解绑定失败!`);
    }
  }

  /**获取指定红点名结果*/
  getRedDotResult(redDotName: string): number {
    const redDotUnitList = RedDotModel.Ins().getRedDotUnitsByName(redDotName);
    let result = 0;
    let redDotType = RedDotType.NONE;
    redDotUnitList?.forEach((redDotUnit) => {
      const redDotUnitResult = this.getRedDotUnitResult(redDotUnit);
      if (redDotUnitResult > 0) {
        const redDotType2 = RedDotModel.Ins().getRedDotUnitRedType(redDotUnit);
        redDotType = redDotType | redDotType2;
        if ((redDotType2 | RedDotType.NUMBER) == RedDotType.NUMBER) result += redDotUnitResult;
      }
    });
    //如果是有红点提示，结果至少是1
    if ((redDotType | RedDotType.RED) == redDotType) result = result <= 0 ? 1 : result;
    return result;
  }

  /**获取指定红点名结果*/
  getRedDotResultAndType(redDotName: string): [number, RedDotType] {
    let result = 0;
    let redDotType = RedDotType.NONE;
    if (RedDotModel.Ins().redDotTrie.getWordNodeActive(redDotName)) {
      const redDotUnitList = RedDotModel.Ins().getRedDotUnitsByName(redDotName);
      redDotUnitList?.forEach((redDotUnit) => {
        const redDotUnitResult = this.getRedDotUnitResult(redDotUnit);
        if (redDotUnitResult > 0) {
          const redDotType2 = RedDotModel.Ins().getRedDotUnitRedType(redDotUnit);
          redDotType = redDotType | redDotType2;
          if ((redDotType2 | RedDotType.NUMBER) == RedDotType.NUMBER) result += redDotUnitResult;
        }
      });
      //如果是有红点提示，结果至少是1
      if ((redDotType | RedDotType.RED) == redDotType) result = result <= 0 ? 1 : result;
    }
    return [result, redDotType];
  }

  /**获取指定红点单元的运算结果*/
  getRedDotUnitResult(redDotUnit: string): number {
    if (!this.unitResultMap.has(redDotUnit)) {
      //LogUtil.e(TAG, `找不到红点运算单元:${redDotUnit}的运算结果!`);
      return 0;
    }
    return this.unitResultMap.get(redDotUnit);
  }

  /**设置指定红点运算单元的运算结果*/
  setRedDotUnitResult(redDotUnit: string, result: number): boolean {
    if (!this.unitResultMap.has(redDotUnit)) {
      LogUtil.e(TAG, `未添加红点运算单元:${redDotUnit}的结果数据, 设置红点运算单元结果失败!`);
      return false;
    }
    this.unitResultMap.set(redDotUnit, result);
    return true;
  }

  /**标记指定红点名脏*/
  markRedDotNameDirty(redDotName: string | string[]): void {
    if (Array.isArray(redDotName)) {
      redDotName.forEach((nn) => {
        const redDotUnitList = RedDotModel.Ins().getRedDotUnitsByName(nn);
        redDotUnitList?.forEach((redDotUnit) => {
          this.markRedDotUnitDirty(redDotUnit);
        });
      });
    } else {
      const redDotUnitList = RedDotModel.Ins().getRedDotUnitsByName(redDotName);
      redDotUnitList?.forEach((redDotUnit) => {
        this.markRedDotUnitDirty(redDotUnit);
      });
    }
  }

  /**标记指定红点运算类型脏*/
  markRedDotUnitDirty(redDotUnit: string | string[]): void {
    if (Array.isArray(redDotUnit)) {
      redDotUnit.forEach((unit) => {
        if (!this.dirtyUnitMap.has(unit)) {
          this.dirtyUnitMap.add(unit);
        }
      });
    } else {
      if (!this.dirtyUnitMap.has(redDotUnit)) {
        this.dirtyUnitMap.add(redDotUnit);
      }
    }
  }

  /** 开启红点 */
  openRedDot(redDotName: TB.GameFunction | TB.GameFunction[] | number | number[]) {
    const result = RedDotModel.Ins().openRedDot(redDotName);
    this.triggerRedDotNameUpdate(result);
  }

  /**更新*/
  updateDirtyRedDot() {
    if (this.dirtyUnitMap.size == 0) return;
    this.checkDirtyRedDotUnit();
  }

  /**检查标脏红点运算单元*/
  private checkDirtyRedDotUnit() {
    this.tempChangedUnitResultList = [];
    this.dirtyUnitMap.forEach((v, k) => {
      if (this.doRedDotUnitCaculate(k)) {
        this.tempChangedUnitResultList.push(k);
      }
    });
    this.dirtyUnitMap.clear();
    //触发已绑定的关心改变红点运算单元的红点刷新
    this.resultChangedRedDotMap.clear();
    const redDotNames = [];
    this.tempChangedUnitResultList.forEach((unit) => {
      const redDotNameList = RedDotModel.Ins().getRedDotUnitNames(unit);
      redDotNameList?.forEach((redDotName) => {
        if (!this.resultChangedRedDotMap.has(redDotName)) {
          //判断红点是否有启用
          if (!RedDotModel.Ins().redDotTrie.getWordNodeActive(redDotName)) return;
          this.resultChangedRedDotMap.set(redDotName, redDotName);
          redDotNames.push(redDotName);
        }
      });
    });
    this.tempChangedUnitResultList = [];
    this.resultChangedRedDotMap.clear();
    //通过每个红点所有影响的红点运算单元得出红点显示结论并通知更新
    this.triggerRedDotNameUpdate(redDotNames);
  }

  /**执行指定红点运算单元计算*/
  private doRedDotUnitCaculate(redDotUnit: string): boolean {
    const preResult = this.getRedDotUnitResult(redDotUnit);
    const redDotUnitFunc = RedDotModel.Ins().getUnitCalculateFunc(redDotUnit);
    let result = 0;
    if (redDotUnitFunc != null) {
      result = redDotUnitFunc();
    } else {
      LogUtil.e(TAG, `未绑定有效计算方法: ${redDotUnit}`);
    }
    this.setRedDotUnitResult(redDotUnit, result);
    return preResult != result;
  }

  /**触发红点名刷新回调*/
  triggerRedDotNameUpdate(redDotName: string | string[]): void {
    if (Array.isArray(redDotName)) {
      redDotName.forEach((nn) => {
        this.triggerRedDotName(nn);
      });
    } else {
      this.triggerRedDotName(redDotName);
    }
  }

  /**触发红点名刷新回调*/
  private triggerRedDotName(redDotName: string) {
    const redDotInfo = RedDotModel.Ins().getRedDotInfoByName(redDotName);
    if (redDotInfo == null) return;

    const redDotUnitList = RedDotModel.Ins().getRedDotUnitsByName(redDotName);
    if (redDotUnitList.length > 0) {
      let result = 0;
      let redDotType = RedDotType.NONE;
      redDotUnitList.forEach((redDotUnit) => {
        const redDotUnitResult = this.getRedDotUnitResult(redDotUnit);
        if (redDotUnitResult > 0) {
          const redDotType2 = RedDotModel.Ins().getRedDotUnitRedType(redDotUnit);
          redDotType = redDotType | redDotType2;
          if ((redDotType2 | RedDotType.NUMBER) == RedDotType.NUMBER) result += redDotUnitResult;
        }
      });
      if ((redDotType | RedDotType.RED) == redDotType) result = result <= 0 ? 1 : result;
      redDotInfo.triggerUpdate(result, redDotType);
    }
  }

  /** 获取红点 */
  getRedDot(layer: GameLayer, size: number): Node {
    const result = LayerManager.Singleton.factory.pop('pre_dot', size);
    Tool.ergodicNoed(result, (node: Node) => {
      node.layer = layer;
    });
    return result;
  }
}
